//
//  SerialPort_t.m
//  Calibration
//
//  Created by ViKing Lai on 2018/1/24.
//  Copyright © 2018年 ViKing Lai. All rights reserved.
//

#import "SerialPort.h"
#import "HiperTimer.h"
#import <IOKit/serial/IOSerialKeys.h>

static NSMutableArray* gPortNames;

@interface SerialPort ()
{
    int             _fileDescriptor;
    double          _restTime;
    NSInteger       _readTimeout;
    NSMutableString *_globalBuffer;
    NSString        *_portname;
    BaudRate        _baudrate;
    DataBits        _databits;
    StopBits        _stopbits;
    Parity          _parity;
    FlowControls    _flowControls;
    InputMode       _inputMode;
    struct termios  _termiosSetting;
    struct termios  _termiosOriginal;
    id<SerialPortDelegate> _delegate;
}
//@property (readwrite) NSString *lastErrInfo;
@property (nonatomic) dispatch_semaphore_t selectSemaphore;
@end

@implementation SerialPort

#pragma mark -
#pragma mark 静态方法

+ (NSArray *)portNames;
{
    static dispatch_once_t once_Token;
    dispatch_once(&once_Token, ^() {
        gPortNames = [[NSMutableArray alloc] init];
    });
    
    [gPortNames removeAllObjects];
    
    CFMutableDictionaryRef dicToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
    
    if(dicToMatch != NULL)
    {
        CFDictionarySetValue(dicToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
        io_iterator_t portIterator;
        kern_return_t rValue = IOServiceGetMatchingServices(kIOMasterPortDefault, dicToMatch, &portIterator);
        
        if(rValue == KERN_SUCCESS)
        {
            io_object_t portEntry;
            
            while((portEntry = IOIteratorNext(portIterator)) != 0)
            {
                CFStringRef bsdPath = (CFStringRef)IORegistryEntryCreateCFProperty(portEntry, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
                
                if(bsdPath)
                {
                    NSString *path = [[NSString alloc]initWithString:(__bridge NSString*)bsdPath];
                    [gPortNames addObject:path];
                    CFRelease(bsdPath);
                }
                
            }
        }
    }
    
    return [gPortNames copy];
}

#pragma mark -
#pragma mark 属性

-  (NSString *)version
{
    NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
    return [infoDic objectForKey:@"CFBundleVersion"];
}

@synthesize newLine = _newLine;
 - (NSString *)newLine
{
    if (_newLine == nil) {
        _newLine = @"\r\n";
    }
    
    return _newLine;
}

- (void)setNewLine:(NSString *)newLine
{
    if (_newLine == newLine) {
        return;
    }
//    [_newLine release];
    _newLine = newLine;
}

- (NSString *)portname
{
    return _portname;
}

- (BOOL)isOpen
{
    return (_fileDescriptor > 0);
}

- (void)setBaudrate:(BaudRate)baudrate
{
    _baudrate = baudrate;
    tcgetattr(_fileDescriptor, &_termiosSetting);
    
    switch (baudrate) {
        case eBaudrate50:    case eBaudrate75:      case eBaudrate110:
        case eBaudrate134:   case eBaudrate150:     case eBaudrate200:
        case eBaudrate300:   case eBaudrate600:     case eBaudrate1200:
        case eBaudrate1800:  case eBaudrate2400:    case eBaudrate4800:
        case eBaudrate9600:  case eBaudrate19200:   case eBaudrate38400:
        case eBaudrate57600: case eBaudrate115200:  case eBaudrate230400:
            cfsetispeed(&_termiosSetting, baudrate);    // 指定输入波特率
            cfsetospeed(&_termiosSetting, baudrate);    // 指定输出波特率
            _termiosSetting.c_cflag |= CREAD;
            _termiosSetting.c_iflag |= CRTS_IFLOW;
            _termiosSetting.c_iflag |= CDTR_IFLOW;
            tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
            break;
        default:
//            ioctl(_fileDescriptor, IOSSIOSPEED, &baudrate);
            break;
    }
}

- (BaudRate)baudrate
{
    if (-1 == tcgetattr(_fileDescriptor, &_termiosSetting)) {
        return eBaudrateInvalid;
    }
   // speed_t iSpeed = cfgetispeed(&_termiosSetting);
    speed_t oSpeed =cfgetospeed(&_termiosSetting);
   _baudrate = (BaudRate)oSpeed;
    return _baudrate;
}

- (void)setDatabits:(DataBits)databits
{
    _databits = databits;
    tcgetattr(_fileDescriptor, &_termiosSetting);
    // Set the character size to the specified value. If the character
    // size is not 8 then it is also important to set ISTRIP. Setting
    // ISTRIP causes all but the 7 low-order bits to be set to
    // zero. Otherwise they are set to unspecified values and may
    // cause problems. At the same time, we should clear the ISTRIP
    // flag when the character size is 8 otherwise the MSB will always
    // be set to zero (ISTRIP does not check the character size
    // setting; it just sets every bit above the low 7 bits to zero).
    if (databits == eDataBits8) {
        _termiosSetting.c_iflag &= ~ISTRIP;     // clear the ISTRIP flag
    } else {
        _termiosSetting.c_iflag |= ISTRIP;      // set the ISTRIP flag
    }
    _termiosSetting.c_cflag &= ~CSIZE;
    
    switch (databits) {                         // 设定串口数据位
        case 5: _termiosSetting.c_cflag |= CS5; break;
        case 6: _termiosSetting.c_cflag |= CS6; break;
        case 7: _termiosSetting.c_cflag |= CS7; break;
        case 8: _termiosSetting.c_cflag |= CS8; break;
        default:
            _lastErrInfo = @"Invalid databits.";
            [self dispatchEncounterError:_lastErrInfo];
            break;
    }
    tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
}

- (DataBits)databits
{
    if (-1 == tcgetattr(_fileDescriptor, &_termiosSetting)) {
        return (_databits = eDataBitsInvalid);
    }
    
    switch (_termiosSetting.c_cflag & CSIZE) {
        case CS5:   return (_databits = eDataBits5);
        case CS6:   return (_databits = eDataBits6);
        case CS7:   return (_databits = eDataBits7);
        case CS8:   return (_databits = eDataBits8);
        default:    return (_databits = eDataBitsDefault);
    }
}

- (void)setStopbits:(StopBits)stopbits
{
    _stopbits = stopbits;
    tcgetattr(_fileDescriptor, &_termiosSetting);
    
    // 设定停止位
    switch (stopbits) {
        case eStopBitsOne:
            _termiosSetting.c_cflag &= ~CSTOPB;
            break;
        case eStopBitsTwo:
            _termiosSetting.c_cflag |= CSTOPB;
            break;
        default:
            _lastErrInfo = @"Invalid stopbits.";
            [self dispatchEncounterError:_lastErrInfo];
            break;
    }
    tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
}

- (StopBits)stopbits
{
    if (-1 == tcgetattr(_fileDescriptor, &_termiosSetting)) {
        return (_stopbits = eStopBitsInvalid);
    }
    
    if (_termiosSetting.c_cflag & CSTOPB) {
        _stopbits = eStopBitsTwo;
    } else {
        _stopbits = eStopBitsOne;
    }
    
    return _stopbits;
}

- (void)setParity:(Parity)parity
{
    _parity = parity;
    tcgetattr(_fileDescriptor, &_termiosSetting);
    // 设定奇偶校验位
    switch (parity) {
        case eParityNone:
            _termiosSetting.c_cflag &= ~PARENB; // parity enable
            _termiosSetting.c_iflag &= ~INPCK;  // enable checking of parity errors
            break;
        case eParityOdd:
            _termiosSetting.c_cflag |= (PARODD | PARENB);
            _termiosSetting.c_iflag |= INPCK;
            break;
        case eParityEven:
            _termiosSetting.c_cflag |= PARENB;
            _termiosSetting.c_cflag &= ~PARODD;
            _termiosSetting.c_iflag |= INPCK;
            break;
        default:
            _lastErrInfo = @"Invlaid parity.";
            [self dispatchEncounterError:_lastErrInfo];
            break;
    }
    tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
}

- (Parity)parity
{
    if (-1 == tcgetattr(_fileDescriptor, &_termiosSetting)) {
        return (_parity = eParityInvalid);
    }
    
    if(_termiosSetting.c_cflag & PARENB) {      // parity is enabled.
        if(_termiosSetting.c_cflag & PARODD) {  // odd parity
            return eParityOdd;
        } else {                                // even parity
            return eParityEven;
        }
    } else {                                    // no parity.
        return eParityNone;
    }

}

- (void)setFlowControls:(FlowControls)flowControls
{
    _flowControls = flowControls;
    tcgetattr(_fileDescriptor, &_termiosSetting);
    // 设定流控制字
    switch (flowControls) {
        case eFlowControlNone:
            _termiosSetting.c_iflag &= ~(IXON | IXOFF);
            _termiosSetting.c_oflag &= ~CRTSCTS;
            break;
        case eFlowControlHard:
            _termiosSetting.c_iflag &= ~(IXON | IXOFF);
            _termiosSetting.c_oflag |= CRTSCTS;
            _termiosSetting.c_cc[VSTART] |= _POSIX_VDISABLE;
            _termiosSetting.c_cc[VSTOP]  |= _POSIX_VDISABLE;
            break;
        case eFlowControlSoft:
            _termiosSetting.c_iflag |= (IXON | IXOFF);
            _termiosSetting.c_oflag |= CRTSCTS;
            _termiosSetting.c_cc[VSTART] |= CTRL_Q;
            _termiosSetting.c_cc[VSTOP]  |= CTRL_S;
            break;
        default:
            _lastErrInfo = @"Invalid flow controls.";
            [self dispatchEncounterError:_lastErrInfo];
            break;
    }
    tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
}

- (FlowControls)flowControls
{
    if (-1 == tcgetattr(_fileDescriptor, &_termiosSetting)) {
        return (_flowControls = eFlowControlInvalid);
    }
    
    if((_termiosSetting.c_iflag & IXON)         &&
       (_termiosSetting.c_iflag & IXOFF)        &&
       (CTRL_Q == _termiosSetting.c_cc[VSTART]) &&
       (CTRL_S == _termiosSetting.c_cc[VSTOP])) {
        _flowControls = eFlowControlSoft;
    } else if (!((_termiosSetting.c_iflag & IXON) ||(_termiosSetting.c_iflag & IXOFF))) {
        if (_termiosSetting.c_cflag & CRTSCTS) {
            // If neither IXON or IXOFF is set then we must have hardware flow control.
            _flowControls = eFlowControlHard;
        } else {
            _flowControls = eFlowControlNone;
        }
    } else {
        _flowControls = eFlowControlDefault;
    }
    
    return _flowControls;
}

- (void)setInputMode:(InputMode)inputMode
{
    if(_fileDescriptor != -1)
    {
        struct termios ts;
        tcgetattr(_fileDescriptor, &ts);
        cfmakeraw(&ts);
        
        ts.c_iflag &= ~eLineMode;    //目标位值0
        ts.c_iflag |= inputMode;
        
        tcsetattr(_fileDescriptor, TCSANOW, &ts);
    }
    
    if(inputMode != _inputMode)
    {
        _inputMode = inputMode;
    }
}

- (InputMode)inputMode
{
    return _inputMode;
}

- (void)setVMin:(cc_t)vmin andVTime:(short)vtime
{
    // I didn't change this issue because i didn't know wether is OK
    // but I know the issue
    tcgetattr(_fileDescriptor, &_termiosSetting);
    _termiosSetting.c_cc[VMIN]  = vmin;
    _termiosSetting.c_cc[VTIME] = vtime;
    tcsetattr(_fileDescriptor, TCSANOW, &_termiosSetting);
}

- (void)setDelegate:(id<SerialPortDelegate>)delegate
{
    if (_delegate != delegate) {
        _delegate = delegate;
    }
}

#pragma mark -
#pragma mark 构造函数

- (id)initWithPath:(NSString *)portname
{
    return [self initWithPath:portname
                     baudrate:eBaudrateDefault
                     databits:eDataBitsDefault
                     stopbits:eStopBitsDefault
                       parity:eParityDefault
                 flowControls:eFlowControlDefault];
}

- (id)initWithPath:(NSString *)portname
          baudrate:(BaudRate)baudrate
          databits:(DataBits)databits
          stopbits:(StopBits)stopbits
            parity:(Parity)parity
      flowControls:(FlowControls)flowControls
{
    self = [super init];
    
    if (self) {
        _fileDescriptor = -1;
        _newLine = @"\r\n";
        _restTime = 0.01;
        _readTimeout = 1000;
        _portname = [[NSString alloc] initWithString:portname];
        _baudrate = baudrate;
        _databits = databits;
        _stopbits = stopbits;
        _parity   = parity;
        _flowControls = flowControls;
        self.selectSemaphore = dispatch_semaphore_create(0);
    }
    
    return self;
}

- (void)dealloc
{
    if (_portname != nil) {
        _portname = nil;
    }
    if (_globalBuffer != nil) {
        _globalBuffer = nil;
    }
    _fileDescriptor = -1;
}

#pragma mark -
#pragma mark 接口函数

- (BOOL)open
{
    return [self open:_portname];
}

- (BOOL)open:(NSString *)portname
{
    return [self open:portname
             baudrate:_baudrate
             databits:_databits
             stopbits:_stopbits
               parity:_parity
         flowControls:_flowControls];
}

- (BOOL)open:(NSString *)portname
    baudrate:(BaudRate)baudrate
    databits:(DataBits)databits
    stopbits:(StopBits)stopbits
      parity:(Parity)parity
flowControls:(FlowControls)flowControls
{
    _lastErrInfo = @"";
    
    BOOL flag = [self isOpen];

    if (!flag) {
        // We only allow three different combinations of ios_base::openmode
        // so we can use a switch here to construct the flags to be used
        // with the open() system call.
        int flags = O_RDWR;  // read and write
        
        // Since we are dealing with the serial port we need to use the
        // O_NOCTTY option.
        flags |= O_NOCTTY | O_EXLOCK;

        // Try to open the serial port.
        _portname = [portname copy];
        
        if (!_portname || [_portname isEqualToString:@""]) {
            _lastErrInfo = @"Device path is empty.";
            [self dispatchEncounterError:_lastErrInfo];
            return flag;
        }
        
        const char *path = [_portname fileSystemRepresentation];
        _fileDescriptor = open(path, flags);
        
        if( _fileDescriptor == -1)
        {
            _lastErrInfo = @"Fail to open serial port.";
            [self dispatchEncounterError:_lastErrInfo];
            return flag;
        }
        
        // Initialize the serial port.
        // Use non-blocking mode while configuring the serial port.
        flags = fcntl(_fileDescriptor, F_GETFL, 0);
        
        if(-1 == fcntl(_fileDescriptor,  F_SETFL, flags | O_NONBLOCK))
        {
            _lastErrInfo = @"Fail to open serial port cause of fail to set nonblock mode.";
            [self dispatchEncounterError:_lastErrInfo];
            [self close];
            return flag;
        }

        // Flush out any garbage left behind in the buffers associated
        // with the port from any previous operations.
        if( -1 == tcflush(_fileDescriptor, TCIOFLUSH) )
        {
            _lastErrInfo = @"Fail to open serial port cause of fail to flush out garbage data.";
            [self dispatchEncounterError:_lastErrInfo];
            [self close];
            return flag;
        }

        // Set up the default configuration for the serial port.
        // struct termios termios_setting;
        [self setBaudrate:baudrate];
        [self setDatabits:databits];
        [self setStopbits:stopbits];
        [self setParity:parity];
        [self setFlowControls:flowControls];
        [self setVMin:1 andVTime:0];
        tcgetattr(_fileDescriptor, &_termiosOriginal);
        // 清除所有正在发生的I/O数据
        tcflush(_fileDescriptor, TCIOFLUSH);
        
        // Allow all further communications to happen in blocking mode.
        flags = fcntl(_fileDescriptor, F_GETFL, 0) ;
        
        if( -1 == fcntl(_fileDescriptor,  F_SETFL, flags & ~O_NONBLOCK))
        {
            [self close];
            return flag;
        }
    }
    
    if ([_delegate respondsToSelector:@selector(serialPortWasOpened:)]) {
        if ([NSThread currentThread] == [NSThread mainThread]) {
            [_delegate performSelector:@selector(serialPortWasOpened:) withObject:self];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [_delegate performSelector:@selector(serialPortWasOpened:) withObject:self];
            });
        }
    }
   
    return (flag = self.isOpen);
}

- (BOOL)close
{
    if (![self isOpen]) { return YES; }
    
    struct termios options;
    tcgetattr(_fileDescriptor, &options);
    options.c_cflag &= ~CRTSCTS;                    // RTS/CTS Flow Control
    options.c_cflag &= ~(CDTR_IFLOW | CDSR_OFLOW);  // DTR/DSR Flow Control
    options.c_cflag &= ~CCAR_OFLOW;                 // DCD Flow Control
    tcsetattr(_fileDescriptor, TCSANOW, &options);
    
    // Set port back the way it was before we used it
    tcsetattr(_fileDescriptor, TCSADRAIN, &_termiosOriginal);
    
    int localFD = _fileDescriptor;
    _fileDescriptor = 0;
    
    if (close(localFD)) {
        _fileDescriptor = localFD;
        
        _lastErrInfo = [NSString stringWithFormat:@"Error closing serial port with file descriptor %i:%i", _fileDescriptor, errno];
        [self dispatchEncounterError:_lastErrInfo];
        return NO;
    }
    
    if ([_delegate respondsToSelector:@selector(serialPortWasClosed:)]) {
        if ([NSThread currentThread] == [NSThread mainThread]) {
            [_delegate performSelector:@selector(serialPortWasClosed:) withObject:self];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [_delegate performSelector:@selector(serialPortWasClosed:) withObject:self];
            });
        }
    }
    
    return YES;
}

- (NSUInteger)bytesToRead
{
    NSUInteger numOfbytes = 0;
    
    if (0 == ioctl(_fileDescriptor, FIONREAD, &numOfbytes)) {
        return numOfbytes;
    } else {
        _lastErrInfo = @"No bytes at out buffer.";
        [self dispatchEncounterError:_lastErrInfo];
        return 0;
    }
}

- (void)writeLine:(NSString *)data
{
    NSString *strData = [NSString stringWithFormat:@"%@%@", data, _newLine];
    const char *bytes = [strData UTF8String];
    NSInteger length = strlen(bytes);
    [self writeBytes:bytes offset:0 count:length];
}

- (void)write:(NSString *)data
{
    const char *bytes = [data UTF8String];
    [self writeBytes:bytes offset:0 count:strlen(bytes)];
}

- (void)write:(NSString *)data encoding:(NSStringEncoding)encoding
{
    const char *bytes = [[data dataUsingEncoding:encoding] bytes];
    [self writeBytes:bytes offset:0 count:strlen(bytes)];
}

- (void)writeData:(NSData *)data
{
    const char *bytes = [data bytes];
    [self writeBytes:bytes offset:0 count:strlen(bytes)];
}

- (void)writeBytes:(const char *)bytes
            offset:(NSInteger)offset
             count:(NSInteger)count
{
    NSInteger length = strlen(bytes);
    
    if (![self isOpen]) { return; }
    
    if (bytes == NULL || bytes == nil) {
        _lastErrInfo = @"Data is NULL.";
        [self dispatchEncounterError:_lastErrInfo];
        return;
    }
    
    if (offset < 0 || count < 0
        || offset > length || count > length
        || (offset + count) > length) {
        _lastErrInfo = @"Parameters is illegal.";
        [self dispatchEncounterError:_lastErrInfo];
        return;
    }
    
    char buffer[count + 1];
    memset(buffer, 0, count + 1);
    
    for (int i = 0; i < count; i++) {
        buffer[i] = bytes[i + offset];
    }
    NSMutableData *dataToSend = [NSMutableData dataWithBytes:buffer length:strlen(buffer)];
    
    while ([dataToSend length] > 0) {
        ssize_t size = write(_fileDescriptor, buffer, count);
        
        if (size < 0) {
            _lastErrInfo = [NSString stringWithFormat:@"Error writing to serial port:%d", errno];
            [self dispatchEncounterError:_lastErrInfo];
        } else if (size > 0){
            [dataToSend replaceBytesInRange:NSMakeRange(0, size) withBytes:NULL length:0];
        }
    }
}

- (char)readChar
{
    char readChar[2] = {'\0', '\0'};
    [self readBytes:readChar offset:0 count:1];
    [self dispatchDoingReceiveData:[NSData dataWithBytes:readChar length:strlen(readChar)]];
    [self dispatchDidReceiveData:[NSData dataWithBytes:readChar length:strlen(readChar)]];
    return readChar[0];
}

- (NSString *)readLine
{
    return [self readTo:_newLine timeout:_readTimeout restInterval:0.01];
}

- (NSString *)readExisting
{
    if (_fileDescriptor < 0) {
        _lastErrInfo = @"Serial port is not opened.";
        [self dispatchEncounterError:_lastErrInfo];
        return @"";
    }
    
    NSMutableString *result = [[NSMutableString alloc] initWithString:@""];
    char buffer[MAX_READ_BUFFER];
    
    while ([self bytesToRead] > 0) {
        memset(buffer, 0, MAX_READ_BUFFER);
        ssize_t length = read(_fileDescriptor, buffer, MAX_READ_BUFFER);
        
        if (length < 0) {
            _lastErrInfo = [NSString stringWithFormat:@"Error reading to serial port:%d", errno];
            [self dispatchEncounterError:_lastErrInfo];
            return result;
        }
        
        [self dispatchDoingReceiveData:[NSData dataWithBytes:buffer length:strlen(buffer)]];
        
        if (!_globalBuffer && [_globalBuffer isNotEqualTo:@""]) {
            [result appendString:_globalBuffer];
            [_globalBuffer setString:@""];
        }
        
        if (strncmp(buffer, "", strlen(buffer))) {
            [result appendString:[NSString stringWithUTF8String:buffer]];
        }
        
        [NSThread sleepForTimeInterval:0.015];
    }
    
    NSString *str = [result copy];
    [self dispatchDidReceiveData:[str dataUsingEncoding:NSUTF8StringEncoding]];

    return result;
}

- (NSData *)readTo:(NSString *)value
             timeout:(NSUInteger)timeout
        restInterval:(NSUInteger)restTime
          usingBlock:(void (^)(NSData *data))block
{
    __block NSMutableData *datas = [NSMutableData data];
    
    if (value == nil) {
        _lastErrInfo = @"Parameters is illegal.";
        return datas;
    }
    if (_fileDescriptor < 0) {
        _lastErrInfo = @"Serial port is not opened.";
        [self dispatchEncounterError:_lastErrInfo];
        return datas;
    }
    
    if (_globalBuffer && [_globalBuffer isNotEqualTo:@""]) {
        [datas appendBytes:[_globalBuffer UTF8String] length:_globalBuffer.length];
        [_globalBuffer setString:@""];
    }
    
    __block NSRange range;
    __block ssize_t length = 0;
    __block NSUInteger time = 0;
    
    dispatch_queue_t pollQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, pollQueue);
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 50 * NSEC_PER_MSEC, 0);
    dispatch_source_set_event_handler(timer, ^{
        if (!self.isOpen) {
            dispatch_semaphore_signal(self.selectSemaphore);
            dispatch_async(pollQueue, ^{ dispatch_source_cancel(timer); });
            return;
        }
        time += 100;
        
        char buffer[MAX_READ_BUFFER];
        memset(buffer, 0, MAX_READ_BUFFER);
        NSData* dataSpec = [value dataUsingEncoding:NSUTF8StringEncoding];
        
        if ([self bytesToRead] > 0) {
            length += read(_fileDescriptor, buffer, MAX_READ_BUFFER);
            NSData * dataFromRead = [NSData dataWithBytes:buffer length:strlen(buffer)];
            if (!dataFromRead) { dataFromRead = [NSData dataWithBytes:@"" length:0]; }
            [datas appendData:dataFromRead];

            if (block) {
                if ([NSThread currentThread] == [NSThread mainThread]) {
                    block(dataFromRead);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{ block(dataFromRead); });
                }
            }
            [self dispatchDoingReceiveData:dataFromRead];
        }
        
        range = [datas rangeOfData:dataSpec
                           options:NSDataSearchBackwards
                             range:NSMakeRange(0, datas.length)];
        
        if (time >= timeout || range.length > 0) {
            dispatch_semaphore_signal(self.selectSemaphore);
            dispatch_async(pollQueue, ^{ dispatch_source_cancel(timer); });
        }
    });
    
    dispatch_resume(timer);
    dispatch_semaphore_wait(self.selectSemaphore, DISPATCH_TIME_FOREVER);
    
    [self dispatchDidReceiveData:datas];
    
    if (range.length > 0) {
        return [datas subdataWithRange:NSMakeRange(0, range.location + range.length)];
    }
    
    return datas;
}

- (NSString *)readTo:(NSString *)value
             timeout:(NSUInteger)timeout
        restInterval:(NSUInteger)restTime
{
    if (value == nil) {
        _lastErrInfo = @"Parameters is illegal.";
        return nil;
    }
    if (_fileDescriptor < 0) {
        _lastErrInfo = @"Serial port is not opened.";
        [self dispatchEncounterError:_lastErrInfo];
        return nil;
    }
    
    NSMutableString *result = [[NSMutableString alloc] init];
    
    if (_globalBuffer && [_globalBuffer isNotEqualTo:@""]) {
        [result appendString:_globalBuffer];
        [_globalBuffer setString:@""];
    }
    
    HiperTimer *timer = [[HiperTimer alloc] init];
    [timer start];
    
    ssize_t length = 0;
    NSRange range = NSMakeRange(0, 0);
    
    while ([timer durationMillisecond] < timeout) {
        char bytes[MAX_READ_BUFFER];
        memset(bytes, 0, MAX_READ_BUFFER);
        
        if ([self bytesToRead] > 0) {
            length += read(_fileDescriptor, bytes, MAX_READ_BUFFER);
            
            if (!strncmp(bytes, "", strlen(bytes))) {
                continue;
            }            
            [result appendString:[NSString stringWithUTF8String:bytes]];
            [self dispatchDoingReceiveData:[NSData dataWithBytes:bytes length:strlen(bytes)]];
            range = [result rangeOfString:value];
            
            if (range.location != NSNotFound) {
                if (!_globalBuffer) {
                    _globalBuffer = [[NSMutableString alloc] init];
                }
                [_globalBuffer appendString:[result substringFromIndex:(range.location + range.length)]];
                break;
            } else {
                range = NSMakeRange(0, 0);
            }
            
            [HiperTimer delayMillsecond:(int)restTime];   // 延时毫秒数
        }
    }
    
    NSData* datas = [result dataUsingEncoding:NSUTF8StringEncoding];
    [self dispatchDidReceiveData:datas];
    
    return [result substringToIndex:range.location + range.length];
}

- (NSInteger)readBytes:(char [])buffer
                offset:(NSInteger)offset
                 count:(NSInteger)count
{
    if (![self isOpen]) {
        return 0;
    }
    
    if (offset < 0 || count < 0) {
        _lastErrInfo = @"Parameters is illegal.";
        return 0;
    }
    
    ssize_t numOfReads = 0;
    char tmpBuffer[count + 1];
    char bytes[MAX(MAX_READ_BUFFER, count) + 1];
    
    memset(tmpBuffer, 0, count + 1);
    memset(bytes, 0, MAX(MAX_READ_BUFFER, count) + 1);
    
    if (buffer == NULL) {
        return 0;
    }

    while ([self bytesToRead] > 0) {
        memset(tmpBuffer, 0, count + 1);
        numOfReads += read(_fileDescriptor, tmpBuffer, (count - numOfReads));
        strcat(bytes, tmpBuffer);
        [self dispatchDoingReceiveData:[NSData dataWithBytes:tmpBuffer length:strlen(tmpBuffer)]];
        
        if (numOfReads >= count) {
            char overbytes[numOfReads - count + 1];
            memset(overbytes, 0, numOfReads - count + 1);
            
            for (NSInteger j = count; j < numOfReads; j++) {
                overbytes[j - count] = bytes[count];
            }
            
            if (!_globalBuffer) {
                _globalBuffer = [[NSMutableString alloc] init];
            }
            [_globalBuffer appendString:[NSString stringWithUTF8String:overbytes]];
            break;
        }
    }
    
    for (int i = 0; i < numOfReads; i++) {
        buffer[i + offset] = bytes[i];
    }
    
    [self dispatchDidReceiveData:[NSData dataWithBytes:buffer length:strlen(buffer)]];
    
    return numOfReads;
}

- (BOOL)discardInBuffer
{
    if (_globalBuffer) {
        [_globalBuffer setString:@""];
    }
    return [self clearBuffer:TCIFLUSH];
}

- (BOOL)discardOutBuffer
{
    return [self clearBuffer:TCOFLUSH];
}

- (BOOL)discardBuffer
{
    return [self clearBuffer:TCIOFLUSH];
}

#pragma mark -
#pragma mark 私有方法

- (BOOL)clearBuffer:(int)bufferQueue
{
    int nRst = 1;
    
    if (_fileDescriptor >= 0) {
        nRst = tcflush(_fileDescriptor, bufferQueue);
    }
    return (nRst == 0);
}

- (void)dispatchDidReceiveData:(NSData *)datas
{
    if ([_delegate respondsToSelector:@selector(serialPort:didReceiveData:)]) {
        if ([NSThread currentThread] == [NSThread mainThread]) {
            [_delegate performSelector:@selector(serialPort:didReceiveData:)
                            withObject:self
                            withObject:datas];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [_delegate performSelector:@selector(serialPort:didReceiveData:)
                                withObject:self
                                withObject:datas];
            });
        }
    }
}

- (void)dispatchDoingReceiveData:(NSData *)datas
{
    if ([_delegate respondsToSelector:@selector(serialPort:doingReceiveData:)]) {
        if ([NSThread currentThread] == [NSThread mainThread]) {
            [_delegate performSelector:@selector(serialPort:doingReceiveData:)
                            withObject:self
                            withObject:datas];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [_delegate performSelector:@selector(serialPort:doingReceiveData:)
                                withObject:self
                                withObject:datas];
            });
        }
    }
}

- (void)dispatchEncounterError:(NSString *)lastErrInfo
{
    if ([_delegate respondsToSelector:@selector(serialPort:didEncounterError:)]) {
        [_delegate performSelector:@selector(serialPort:didEncounterError:)
                        withObject:self
                        withObject:lastErrInfo];
    }
}

@end
